﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using eProject.Model;

namespace eProject.ViewModel
{
    public class TaskViewModel : ViewModelBase
    {
        private readonly CommonManager _manager;

        internal Task Task { get; private set; }

        internal TaskViewModel(CommonManager manager, Task task )
        {
            if (manager == null) throw new ArgumentNullException("manager");
            if (task == null) throw new ArgumentNullException("task");

            _manager = manager;

            _manager.ManHoursChanged += OnManHoursChanged;        
           
            Task = task;

            INotifyCollectionChanged itasks = _manager.Tasks;
            itasks.CollectionChanged += OnTasksCollectionChanged;

            INotifyCollectionChanged iactivities = _manager.Activities;
            iactivities.CollectionChanged += OnActivitiesCollectionChanged;

            RefreshPeriod();
            RefreshManHours();

            IsUnlinked = false;

        }

        public string Name
        {
            get { return Task.Name; }
            set
            {
                if (value == null) throw new ArgumentNullException("value");
                if (value == "") throw new ArgumentException("Name is empty string");
                if (value == Task.Name) return;
                Task.Name = value;
                InvokePropertyChanged("Name");
            }
        }

        public string Description
        {
            get { return Task.Description; }
            set
            {
                if (value == null) throw new ArgumentNullException("value");                
                if (value == Task.Description) return;

                Task.Description = value;
                InvokePropertyChanged("Description");
            }
        }

        public long Duration
        {
            get { return Task.Duration; }
            set
            {                
                if (value <= 0) throw new ArgumentException("Duration must be greater than zero");
                if (value == Task.Duration) return;

                Task.Duration = value;
                InvokePropertyChanged("Duration");
                RefreshProgress();
            }
        }

        public double ManHours
        {
            get; private set;
        }

        public double Progress
        {
            get; private set;
        }
        
        public Period Period { get; private set; }

        public int Index { get { return _manager.Tasks.IndexOf(this); } }
        
        internal void InitializeAfterRestore()
        {
        	RefreshManHours();
        	RefreshProgress();
        }
        
        private void RefreshProgress()
        {
			Progress = ManHours / Duration * 100;
            InvokePropertyChanged("Progress");        	
        }
        
        private void RefreshManHours()
        {
            var manHours = _manager.GetManHours(this);
            if (ManHours == manHours) return;
            ManHours = manHours;            
            InvokePropertyChanged("ManHours");            
        }

        private void OnManHoursChanged(IEnumerable<Activity> activities)
        {
            if (activities.Any(a => a.Task == Task))
            {
                RefreshManHours();
                RefreshProgress();
            }
        }

        private void OnTasksCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            InvokePropertyChanged("Index");
        }

        private void OnActivitiesCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
        {
            RefreshPeriod();
            RefreshManHours();
            RefreshProgress();
        }

        private void RefreshPeriod()
        {
            var activities = _manager.Activities.Where(a => a.Task == this);
            Period period = null;
            if (activities.Count() > 0)
            {
                var end = activities.Max(a => a.Period.End);
                var begin = activities.Min(a => a.Period.Begin);
                period = new Period(begin,end);                
            }

            if (period == Period) return;
            Period = period;
            InvokePropertyChanged("Period");
        }

        public bool IsUnlinked { get; private set; }

        public void Unlink()
        {
            if (IsUnlinked) return;

            _manager.ManHoursChanged -= OnManHoursChanged;
            INotifyCollectionChanged itasks = _manager.Tasks;
            itasks.CollectionChanged -= OnTasksCollectionChanged;

            INotifyCollectionChanged iactivities = _manager.Activities;
            iactivities.CollectionChanged -= OnActivitiesCollectionChanged;

            IsUnlinked = true;
        }
    }

}
